//
//  HttpManager.m
//  jetplayerTestProject
//
//  Created by ott on 14-7-26.
//  Copyright (c) 2014年 ott. All rights reserved.
//

#import "HttpManager.h"
#import "SPRequest.h"
//#import <SPKit/SPMacros.h>
#import "NSObject+NSJSONSerialization.h"
#import "HttpConectionObject.h"

#define MAX_WORKING_NUMBER 3

#define TURE    1

static HttpManager *managerInstance = nil;

@interface HttpManager()<HttpConectionDelegate>
{
    NSMutableArray *_highQueue;
    NSMutableArray *_normalQueue;
    NSMutableArray *_lowQueue;
    NSMutableArray *_workingQueue;
    NSThread *_runingThread;
    NSCondition *_lock;
    NSInteger _lowLevelNumber;
}
@end

@implementation HttpManager

+(id) shareInstance
{
    @synchronized(self){
        if (!managerInstance)
        {
            managerInstance = [[HttpManager alloc] init];
        }
    }
    return managerInstance;
}

+ (void) sendRequest:(SPRequest *)request
{
    [[HttpManager shareInstance] sendRequest:request];
}

+ (void) cancelWithRequest:(SPRequest *)request
{
    [[HttpManager shareInstance] cancelWithRequest:request];
}

+ (void) cancelWithOwner:(id<HttpManagerDelegate>)delegate
{
    [[HttpManager shareInstance] cancelWithOwner:delegate];
}

+ (void) cancelAllRequest
{
    [[HttpManager shareInstance] cancelAllRequest];
}

- (void) dealloc
{
//    [_lock release];
    [_runingThread cancel];
//    [_runingThread release];
//    [_highQueue release];
//    [_normalQueue release];
//    [_lowQueue release];
//    [_workingQueue release];
//    [super dealloc];
}

- (id) init
{
    if (self = [super init])
    {
        _highQueue = [[NSMutableArray alloc] init];
        _normalQueue = [[NSMutableArray alloc] init];
        _lowQueue = [[NSMutableArray alloc] init];
        _workingQueue = [[NSMutableArray alloc] initWithCapacity:MAX_WORKING_NUMBER];
        _lock = [[NSCondition alloc] init];
        _lowLevelNumber = 0;
        
        _runingThread = [[NSThread alloc] initWithTarget:self selector:@selector(runing) object:nil];
        [_runingThread start];
    }
    return self;
}

- (NSMutableArray *)queueWithLevel:(HTTP_LEVEL)level
{
    switch (level) {
        case HTTP_LEVEL_HIGH:
            return _highQueue;
        case HTTP_LEVEL_LOW:
            return _lowQueue;
        case HTTP_LEVEL_NORMAL:
            return _normalQueue;
        default:
            return nil;
    }
}

- (void) sendRequest:(SPRequest *)request;
{
    [_lock lock];
        
    HttpConectionObject *httpConection = [[HttpConectionObject alloc] initWithRequest:request delegate:self];
    [[self queueWithLevel:request.level] addObject:httpConection];
//    [httpConection release];
    
    [_lock signal];
    [_lock unlock];
}

- (void) cancelWithRequest:(SPRequest *)request
{
    [_lock lock];
    
    NSMutableArray *currentQueue = [self queueWithLevel:request.level];
    for (HttpConectionObject *waitingConection in currentQueue)
    {
        if (waitingConection.request == request)
        {
            [currentQueue removeObject:waitingConection];
            break;
        }
    }
    
    for (HttpConectionObject *workingConection in _workingQueue)
    {
        if (workingConection.request == request)
        {
            [workingConection cancel];
            break;
        }
    }
    
    [_lock signal];
    [_lock unlock];
}

- (void) cancelWithOwner:(id<HttpManagerDelegate>)delegate;
{
    [_lock lock];
    
    for (NSInteger i = _highQueue.count - 1; i >= 0; i--)
    {
        HttpConectionObject *waitingConection = [_highQueue objectAtIndex:i];
        if (waitingConection.request.delegate == delegate)
        {
            [_highQueue removeObject:waitingConection];
            waitingConection = nil;
        }
    }
    
    for (NSInteger i = _normalQueue.count - 1; i >= 0; i--)
    {
        HttpConectionObject *waitingConection = [_normalQueue objectAtIndex:i];
        if (waitingConection.request.delegate == delegate)
        {
            [_normalQueue removeObject:waitingConection];
            waitingConection = nil;
        }
    }
    
    for (NSInteger i = _lowQueue.count - 1; i >= 0; i--)
    {
        HttpConectionObject *waitingConection = [_lowQueue objectAtIndex:i];
        if (waitingConection.request.delegate == delegate)
        {
            [_lowQueue removeObject:waitingConection];
            waitingConection = nil;
        }
    }
    
    for (NSInteger i = _workingQueue.count - 1; i >= 0; i--)
    {
        HttpConectionObject *workingConection = [_workingQueue objectAtIndex:i];
        if (workingConection.request.delegate == delegate)
        {
            [workingConection cancel];
        }
    }
    
    [_lock signal];
    [_lock unlock];
}

- (void) cancelAllRequest
{
    [_lock lock];
    
    [_highQueue removeAllObjects];
    [_normalQueue removeAllObjects];
    [_lowQueue removeAllObjects];
    
    for (NSInteger i=_workingQueue.count-1; i>=0; i--)
    {
        HttpConectionObject *workingConection = [_workingQueue objectAtIndex:i];
        [workingConection cancel];
    }
    
    [_lock signal];
    [_lock unlock];
}


- (void) runing
{
    while (TURE) {
        
//        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        @autoreleasepool {
            
            [_lock lock];
            
            NSInteger allCount = _highQueue.count + _normalQueue.count + _lowQueue.count;
            NSInteger highLevelCount = _highQueue.count + _normalQueue.count;
            if (allCount == 0 || (_workingQueue.count >= MAX_WORKING_NUMBER-2 && highLevelCount == 0) || _workingQueue.count == MAX_WORKING_NUMBER)
            {
                [_lock wait];
            }
            
            
            allCount = _highQueue.count + _normalQueue.count + _lowQueue.count;
            if (allCount > 0 && _workingQueue.count < MAX_WORKING_NUMBER)
            {
    //            [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
                
                if (_highQueue.count > 0)
                {
                    HttpConectionObject *conection = [_highQueue objectAtIndex:0];
                    [_workingQueue addObject:conection];
                    [_highQueue removeObject:conection];
                    [conection start];
                }
                else if (_normalQueue.count > 0)
                {
                    HttpConectionObject *conection = [_normalQueue objectAtIndex:0];
                    [_workingQueue addObject:conection];
                    [_normalQueue removeObject:conection];
                    [conection start];
                }
                else if (_lowQueue.count > 0)
                {
                    if (_lowLevelNumber < MAX_WORKING_NUMBER-2)
                    {
                        HttpConectionObject *conection = [_lowQueue objectAtIndex:0];
                        [_workingQueue addObject:conection];
                        [_lowQueue removeObject:conection];
                        [conection start];
                        
                        ++_lowLevelNumber;
                    }
                }
            }
            
            [_lock unlock];
        }
//        [pool release];
    }
}

#pragma mark -
#pragma mark HttpConectionDelegate
- (void) httpConection:(HttpConectionObject *)conection didFinish:(NSData *)data
{
//    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    //确保回到主线程回调
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([conection.request.delegate respondsToSelector:@selector(request:didFinish:)])
        {
            [conection.request.delegate request:conection.request didFinish:data];
        }
        else if (conection.request.callback)
        {
            conection.request.callback(YES, nil, data);
        }
        
        //将该任务从工作队列推出
        [_lock lock];
        
        [_workingQueue removeObject:conection];
        if (conection.request.level == HTTP_LEVEL_LOW)
            --_lowLevelNumber;
        
        [_lock signal];
        [_lock unlock];
        
    });
}

- (void) httpConection:(HttpConectionObject *)conection totalBytes:(NSUInteger)totalBytes sendBytes:(NSUInteger)sendBytes
{
    //确保回到主线程回调
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([conection.request.delegate respondsToSelector:@selector(request:totalBytes:sendBytes:)])
        {
            [conection.request.delegate request:conection.request totalBytes:totalBytes sendBytes:sendBytes];
        }
    });
}

- (void) httpConection:(HttpConectionObject *)conection didFaild:(NSError *)error
{
//    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];

    //确保回到主线程回调
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([conection.request.delegate respondsToSelector:@selector(request:didFailed:)])
        {
            [conection.request.delegate request:conection.request didFailed:error];
        }
        else if (conection.request.callback)
        {
            conection.request.callback(NO, error, nil);
        }
        
        //将该任务从工作队列推出
        [_lock lock];
        
        [_workingQueue removeObject:conection];
        if (conection.request.level == HTTP_LEVEL_LOW)
            --_lowLevelNumber;
        
        [_lock signal];
        [_lock unlock];
    });
}

- (void) httpConectionHasCanceled:(HttpConectionObject *)conection
{
//    [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
    //确保回到主线程回调
    dispatch_async(dispatch_get_main_queue(), ^{
//        if ([conection.m_request.m_delegate respondsToSelector:@selector(requestHasBeenCanceld:)])
//        {
//            [conection.m_request.m_delegate requestHasBeenCanceld:conection.m_request];
//        }
//        else if (conection.m_request.m_callback)
//        {
//            conection.m_request.m_callback(NO, nil, nil);
//        }
        
        //将该任务从工作队列推出
        [_lock lock];
        
        [_workingQueue removeObject:conection];
        if (conection.request.level == HTTP_LEVEL_LOW)
            --_lowLevelNumber;
        
        [_lock signal];
        [_lock unlock];
    });
}
@end


@implementation HttpManager (HTTPRequest)


// the encoding default is NSUTF8StringEncoding.
/// POST json字符串 完成回调block
+ (void) postJson:(NSString *)json toURL:(NSString *)url completion:(CallbackBlock)complete{
    [self postJson:json toURL:url level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) postJson:(NSString *)json toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    [self postJson:json toURL:url level:level charset:HTTPCharsetUTF8 completion:complete];
}
+ (void) postJson:(NSString *)json toURL:(NSString *)url level:(HTTP_LEVEL)level charset:(HTTPCharset)set completion:(CallbackBlock)complete{
    [self postString:json toURL:url level:level completion:complete contentType:HTTPContentTypeJSON charset:set];
}

/// POST json字符串 完成回调代理
+ (void) postJson:(NSString *)json toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate{
    [self postJson:json toURL:url delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) postJson:(NSString *)json toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    [self postJson:json toURL:url delegate:delegate level:level charset:HTTPCharsetUTF8];
}
+ (void) postJson:(NSString *)json toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level charset:(HTTPCharset)set{
    [self postString:json toURL:url delegate:delegate level:level contentType:HTTPContentTypeJSON charset:set];
}


+ (void) postString:(NSString *)string toURL:(NSString *)url completion:(CallbackBlock)complete{
    [self postString:string toURL:url level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    [self postString:string toURL:url level:level completion:complete contentType:HTTPContentTypeDefault];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete contentType:(HTTPContentType)type{
    [self postString:string toURL:url level:level completion:complete contentType:type charset:HTTPCharsetUTF8];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete contentType:(HTTPContentType)type charset:(HTTPCharset)set{
    NSStringEncoding coding;
    switch (set) {
        case HTTPCharsetBIG5:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingBig5);
            break;
        case HTTPCharsetGB18030:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
            break;
        case HTTPCharsetGB2312:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_2312_80);
            break;
        case HTTPCharsetGBK:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGBK_95);
            break;
        case HTTPCharsetUTF8:
        default:
            coding = NSUTF8StringEncoding;
            break;
    }
    [self postData:[string dataUsingEncoding:coding] toURL:url level:level completion:complete contentType:type charset:set];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate{
    [self postString:string toURL:url delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    [self postString:string toURL:url delegate:delegate level:level contentType:HTTPContentTypeDefault];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level contentType:(HTTPContentType)type{
    [self postString:string toURL:url delegate:delegate level:level contentType:type charset:HTTPCharsetUTF8];
}
+ (void) postString:(NSString *)string toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level contentType:(HTTPContentType)type charset:(HTTPCharset)set{
    NSStringEncoding coding;
    switch (set) {
        case HTTPCharsetBIG5:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingBig5);
            break;
        case HTTPCharsetGB18030:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
            break;
        case HTTPCharsetGB2312:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_2312_80);
            break;
        case HTTPCharsetGBK:
            coding = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGBK_95);
            break;
        case HTTPCharsetUTF8:
        default:
            coding = NSUTF8StringEncoding;
            break;
    }
    [self postData:[string dataUsingEncoding:coding] toURL:url delegate:delegate level:level contentType:type charset:set];
}


// the encoding default is NSUTF8StringEncoding.
/// POST NSData 完成回调block
+ (void) postData:(NSData *)data toURL:(NSString *)url completion:(CallbackBlock)complete{
    [self postData:data toURL:url level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    [self postData:data toURL:url level:level completion:complete contentType:HTTPContentTypeDefault];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete contentType:(HTTPContentType)type{
    [self postData:data toURL:url level:level completion:complete contentType:type charset:HTTPCharsetUTF8];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete contentType:(HTTPContentType)type charset:(HTTPCharset)set{
    [self postData:data toUrl:url level:level delegate:nil charset:set ContentType:type completion:complete];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate{
    [self postData:data toURL:url delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    [self postData:data toURL:url delegate:delegate level:level  contentType:HTTPContentTypeDefault];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level contentType:(HTTPContentType)type{
    [self postData:data toURL:url delegate:delegate level:level contentType:HTTPContentTypeDefault charset:HTTPCharsetUTF8];
}
+ (void) postData:(NSData *)data toURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level contentType:(HTTPContentType)type charset:(HTTPCharset)set{
    [self postData:data toUrl:url level:level delegate:delegate charset:set ContentType:type completion:nil];
}


+ (void)postData:(NSData *)body
           toUrl:(NSString *)url
              level:(HTTP_LEVEL)level
           delegate:(id)delegate
            charset:(HTTPCharset)set
        ContentType:(HTTPContentType)type
         completion:(CallbackBlock)complete
{
    if (level < HTTP_LEVEL_HIGH ||
        level > HTTP_LEVEL_LOW) {
        level = HTTP_LEVEL_NORMAL;
    }
    NSString *contentType;
    if (type > HTTPContentTypeDefault &&
        type <= HTTPContentTypeSTREAM) {
        contentType = [self contentTypeHeaderWith:type set:HTTPCharsetGBK];
    }else{
        contentType = @"application/x-www-form-urlencoded";
    }
    
    SPRequest *req = [SPRequest requestObjectWithUrlString:url];
    req.level = level;
    [req addValue:contentType forHTTPHeaderField:@"Content-Type"];
    [req addValue:@(body.length).stringValue forHTTPHeaderField:@"Content-Length"];
    
    req.HTTPMethod = @"POST";
    req.HTTPBody = body;
    
    if (complete) {
        req.callback = complete;
    }else if (delegate){
        req.delegate = delegate;
    }else{
        
    }
    [self sendRequest:req];
}

// ******************** GET **************************** //
/// GET 数据 完成回调block
+ (void) getFromURL:(NSString *)url completion:(CallbackBlock)complete{
    [self getFromURL:url level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) getFromURL:(NSString *)url level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    [self getFromURL:url level:level complete:complete delegate:nil];
}

+ (void) getFromURL:(NSString *)url paramsString:(NSString *)string completion:(CallbackBlock)complete{
    [self getFromURL:url paramsString:string level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) getFromURL:(NSString *)url paramsString:(NSString *)string level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    if (string && string.length) {
        url = [NSString stringWithFormat:@"%@?%@",url,string];
    }
    [self getFromURL:url level:level completion:complete];
}

+ (void) getFromURL:(NSString *)url withParams:(NSDictionary *)params completion:(CallbackBlock)complete{
    [self getFromURL:url withParams:params level:HTTP_LEVEL_NORMAL completion:complete];
}
+ (void) getFromURL:(NSString *)url withParams:(NSDictionary *)params level:(HTTP_LEVEL)level completion:(CallbackBlock)complete{
    if (params && params.count) {
        url = [NSString stringWithFormat:@"%@?%@",url,[self dictionaryToString:params]];
    }
    [self getFromURL:url level:level complete:complete delegate:nil];
}

/// GET 数据 完成回调代理
+ (void) getFromURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate{
    [self getFromURL:url delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) getFromURL:(NSString *)url delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    [self getFromURL:url level:level complete:nil delegate:delegate];
}

+ (void) getFromURL:(NSString *)url paramsString:(NSString *)string delegate:(id<HttpManagerDelegate>)delegate{
    [self getFromURL:url paramsString:string delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) getFromURL:(NSString *)url paramsString:(NSString *)string delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    if (string && string.length) {
        url = [NSString stringWithFormat:@"%@?%@",url,string];
    }
    [self getFromURL:url delegate:delegate level:level];
}

+ (void) getFromURL:(NSString *)url withParams:(NSDictionary *)params delegate:(id<HttpManagerDelegate>)delegate{
    [self getFromURL:url withParams:params delegate:delegate level:HTTP_LEVEL_NORMAL];
}
+ (void) getFromURL:(NSString *)url withParams:(NSDictionary *)params delegate:(id<HttpManagerDelegate>)delegate level:(HTTP_LEVEL)level{
    if (params && params.count) {
         url = [NSString stringWithFormat:@"%@?%@",url,[self dictionaryToString:params]];
    }
    [self getFromURL:url level:level complete:nil delegate:delegate];
}



+(void)getFromURL:(NSString *)url level:(HTTP_LEVEL)level complete:(CallbackBlock)complete delegate:(id)delegate{
    SPRequest *req = [SPRequest requestObjectWithUrlString:url];
    req.level = level;

    req.HTTPMethod = @"GET";
    if (complete) {
        req.callback = complete;
    }else if (delegate){
        req.delegate = delegate;
    }

    [self sendRequest:req];
}

+(NSString *)dictionaryToString:(NSDictionary *)dic{
    NSMutableString *path = [NSMutableString string];
    NSArray *args = [dic allKeys];
    for (NSString *key in args){
        [path appendString:[NSString stringWithFormat:@"%@=%@&",key,dic[key]]];
    }
    [path deleteCharactersInRange:NSMakeRange(path.length - 1, 1)];
    return [path stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
}



+ (NSString *)contentTypeHeaderWith:(HTTPContentType)type set:(HTTPCharset)set{
    return [NSString stringWithFormat:@"%@;charset=%@",[self getContentTypeValueByType:type],[self getCharsetNameBySet:set]];
}
+ (NSString *)getContentTypeValueByType:(HTTPContentType)type{
    switch (type) {
        case HTTPContentTypeDefault:
            return @"application/x-www-form-urlencoded";
            break;
        case HTTPContentTypeAtomXML:
            return @"application/atom+xml";
            break;
        case HTTPContentTypeGIF:
            return @"image/gif";
            break;
        case HTTPContentTypeHTML:
            return @"text/html";
            break;
        case HTTPContentTypeJPG:
            return @"image/jpeg";
            break;
        case HTTPContentTypePDF:
            return @"application/pdf";
            break;
        case HTTPContentTypePNG:
            return @"image/png";
            break;
        case HTTPContentTypeSTREAM:
            return @"application/octet-stream";
            break;
        case HTTPContentTypeTEXT:
            return @"text/plain";
            break;
        case HTTPContentTypeWORD:
            return @"application/msword";
            break;
        case HTTPContentTypeXHTML:
            return @"application/xhtml+xml";
            break;
        case HTTPContentTypeXML:
            return @"application/xml";
            break;
        case HTTPContentTypeXML_TEXT:
            return @"text/xml";
            break;
        case HTTPContentTypeJSON:
        default:
            return @"application/json";
            break;
    }
}
+(NSString *)getCharsetNameBySet:(HTTPCharset)set{
    switch (set) {
        case HTTPCharsetUTF8:
            return @"utf-8";
            break;
        case HTTPCharsetGBK:
            return @"gbk";
            break;
        case HTTPCharsetGB2312:
            return @"gb2312";
            break;
        case HTTPCharsetGB18030:
            return @"gb18030";
            break;
        case HTTPCharsetBIG5:
            return @"big-5";
            break;
        default:
            return @"utf-8";
            break;
    }
}

@end
